<?php


namespace App\Admin\Override;

use App\Admin\Override\Field\Decimal;
use App\Admin\Override\Field\HtmlFreely;
use App\Admin\Override\Field\ImageView;
use App\Admin\Override\Field\MultipleFileView;
use App\Admin\Override\Field\MultipleImageView;
use App\Admin\Override\Field\DatetimeHour;
use App\Cache\Maintenance;
use App\Http\Logic\Common\Audit;
use App\Utils\Helper;
use Closure;
use Dcat\Admin\Admin;
use Dcat\Admin\Form as Base;
use Dcat\Admin\Tree;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

/**
 * @method MultipleImageView multipleImageView($column, $label = '')
 * @method MultipleFileView multipleFileView($column, $label = '')
 * @method HtmlFreely htmlFreely($html,$label = '')
 * @method ImageView imageView($html,$label = '')
 * @method Divide divider(string $title = null)
 * @method Decimal decimal($column, $label = '')
 * @method DateTimeHour datetimeHour($column, $label = '')
 * @method Expend expend(string $title = null)
 * @method FileExpend fileExpend($label = '',$files=null)
 */
class Step extends Base
{
    protected static $AuditorArr = [];

    public function __construct(
        $repository = null,
        ?Closure $callback = null,
        Request $request = null
    )
    {
        parent::__construct($repository, $callback, $request);
        $this->showDeleteButton()
            ->disableCreatingCheck()
            ->disableEditingCheck()
            ->disableViewCheck()
            ->disableViewButton()
            ->disableListButton()
            ->disableDeleteButton()
            ->disableResetButton();

        self::$availableFields['multipleFileView'] = MultipleFileView::class;
        self::$availableFields['imageView'] = ImageView::class;
        self::$availableFields['htmlFreely'] = HtmlFreely::class;
        self::$availableFields['divider'] = Divide::class;
        self::$availableFields['decimal'] = Decimal::class;
        self::$availableFields['datetimeHour'] = DateTimeHour::class;
        self::$availableFields['expend'] = Expend::class;
        self::$availableFields['fileExpend'] = FileExpend::class;

    }

    public function showDeleteButton(bool $show = true): Step
    {
        if(Admin::user()){
            $this->builder->tools()->disableDelete(
                !$show
                ||
                Admin::user()->cannot(
                    Helper::resolvePermissionRouteName('destroy')
                )
            );
        }


        return $this;
    }

    /**
     * @param string $field
     * @param array $actions
     */
    public function disableButtonOnMaintenance(
        string $field,
        array  $actions = ['store', 'update', 'destroy']
    )
    {
        if (Maintenance::isNotMaintenance($field)) {
            if (in_array('store', $actions)) {
                $this->disableCreatingCheck();
            }
            if (in_array('update', $actions)) {
                $this->disableEditingCheck();
            }
            if (in_array('store', $actions) && in_array('update', $actions)) {
                $this->disableSubmitButton();
            }
            if (in_array('destroy', $actions)) {
                $this->disableDeleteButton();
            }
        }
    }

    /**
     * @param string $field
     * @param int $auditType
     * @param Controller $controller
     * @param string $name if start with _ means name field
     * @param string $auditDetail
     * @param bool $nameWithField
     * @param array $extra
     * @param string|null $redirect
     * @param string $brief
     * @return Form
     */
    public function auditEvent(
        string     $field,
        int        $auditType,
        Controller $controller,
        array      $auditors = [],
        string     $name = '_name',
                   $auditDetail = '',
        bool       $nameWithField = true,
        array      $extra = [],
        string     $redirect = null,
        string     $brief = ''
    ): Step
    {
        $_this = $this;
        $this->saving(function (Step $form) use (
            $field,
            $auditType,
            $controller,
            $name,
            $auditDetail,
            $nameWithField,
            $extra,
            $redirect,
            $brief,
            $_this,
            $auditors
        ) {
            $data = $form->inputs;
            if ($form->isCreating()) {
                $dataType = Audit::DATA_TYPE_CREATE;
                $auditDetailField = 'create';
                $name = $_this->resolveAuditName($data, $name);
            } else {
                // when save order do not have resource id
                $id = $form->builder->getResourceId();
                if ($id) {
                    $data = array_merge($form->inputs, ['_id' => $id]);
                    $name = $_this->resolveAuditName($form->model()->toArray(), $name);
                }

                $dataType = Audit::DATA_TYPE_UPDATE;
                $auditDetailField = 'edit';
            }
            if (is_array($auditDetail)) {
                $auditDetail = $auditDetail[$auditDetailField] ?? '';
            }

            try {
                $data = $controller->auditPrepareData($data);
                $has_name = $data['name'] ?? false;
                if(empty($name) && $has_name){
                    $name = $data['name'];
                }
            } catch (Exception $e) {
                return Admin::json([
                    'file' => $e->getFile(),
                    'line' => $e->getLine()
                ])->error($e->getMessage());
            }
            $result = self::makeAudit(
                $field,
                $auditType,
                $data,
                $dataType,
                $controller,
                $auditors,
                $name,
                $auditDetail,
                $nameWithField,
                $extra,
                $brief
            );

            return $result;
        });

        $this->deleting(function (Step $form) use (
            $field,
            $auditType,
            $controller,
            $name,
            $auditDetail,
            $nameWithField,
            $extra,
            $redirect,
            $brief,
            $_this,
            $auditors
        ) {
            $nameArr = [];
            $model = $form->model()->toArray();
            foreach ($model as $v) {
                $n = $controller->auditDeleteName($v);
                $nameArr[] = is_null($n)
                    ? $_this->resolveAuditName($v, $name)
                    : $n;
            }
            $nameStr = '【' . implode('】【', $nameArr) . '】';
            $nameLength = mb_strlen($nameStr);
            if ($nameLength > 50) {
                $count = count($nameArr);
                if ($count > 1) {
                    $name = trans('common.audit_delete_name_long', [
                        'name' => $nameArr[0],
                        'count' => $count
                    ]);
                } else {
                    $name = Str::limit($nameStr);
                }
            } else {
                $name = $nameStr;
            }

            if (is_array($auditDetail) && isset($auditDetail['delete'])) {
                $auditDetail = $auditDetail['delete'];
            } else {
                $auditDetail = $auditDetail ?:
                    $_this->resolveAuditDetail($nameLength, $nameStr, $nameArr);
            }
            $id = explode(',', $form->builder->getResourceId());

            return self::makeAudit(
                $field,
                $auditType,
                ['_id' => $id],
                Audit::DATA_TYPE_DELETE,
                $controller,
                $auditors,
                $name,
                $auditDetail,
                $nameWithField,
                $extra,
                $brief
            );
        });

        return $this;
    }

    /**
     * @param array $data
     * @param string $name
     * @return string
     */
    private function resolveAuditName(array $data, string $name): string
    {
        $hasSeparator = str_starts_with($name, '#');
        $names = explode('|', ltrim($name, '|'));
        $separator = $hasSeparator ? substr(array_shift($names), 1) : '';
        $str = [];
        foreach ($names as $v) {
            if (str_starts_with($v, '_')) {
                $str[] = $data[substr($v, 1)] ?? '';
            } else {
                $str[] = $v;
            }
        }

        return implode($separator, array_filter($str));
    }

    /**
     * @param int $nameLength
     * @param string $name
     * @param array $nameArr
     * @return string
     */
    private function resolveAuditDetail(int $nameLength, string $name, array $nameArr): string
    {
        if ($nameLength > 255) {
            $count = count($nameArr);
            if ($count > 1) {
                $offset = mb_strrpos(mb_substr($name, 0, 200), '】');
                return trans('common.audit_delete_name_long', [
                    'name' => mb_substr($name, 0, $offset + 1),
                    'count' => $count
                ]);
            }

            return Str::limit($name, 250);
        }

        return $name;
    }

    /**
     * @param string $field
     * @param int $auditType
     * @param Controller $controller
     * @param int $dataType
     * @param array $data
     * @param string $name
     * @param string $auditDetail
     * @param bool $nameWithField
     * @param array $extra
     * @param string|null $redirect
     * @param string $brief
     * @return mixed
     */
    public static function makeAudit(
        string $field,
        int    $auditType,
        array  $data,
        int    $dataType,
        Controller $controller,
        array  $auditors = [],
        string $name = '',
        string $auditDetail = '',
        bool   $nameWithField = true,
        array  $extra = [],
        string $brief = '',
        string     $redirect = null
    )
    {
        if(isset($controller->auditBefore) && $controller->auditBefore){
            $has_audit_before = $data['audit_before_id'] ?? false;
            if(!$has_audit_before || empty($data['audit_before_id'])){
                DB::rollBack();
                //存json
                $model = Audit::makeAuditsBefore(
                    $field,
                    $auditType,
                    Admin::user()->id,
                    $data,
                    $dataType,
                    get_class($controller),
                    $auditors,
                    $name,
                    $auditDetail,
                    $nameWithField,
                    $extra,
                    $brief
                );

                return $model ? Admin::json()->success('')->data(['audit_id'=>$model['id']])
                    ->redirect($redirect ?? Helper::getCurrentPageIndexUrl())
                    : null;
            }
        }
        try {
            $result = Audit::makeAuditors(
                $field,
                $auditType,
                Admin::user()->id,
                $data,
                $dataType,
                get_class($controller),
                $auditors,
                $name,
                $auditDetail,
                $nameWithField,
                $extra,
                $brief
            );
            return $result ? Admin::json()->success(trans('common.submit_wait_audit'))->data(['audit_id'=>$result['id']])
                ->redirect($redirect ?? Helper::getCurrentPageIndexUrl())
                : null;
        } catch (Exception $e) {
            return Admin::json([
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ])->error($e->getMessage());
        }
    }

    /**
     * 审核人
     * @param array $author
     * @return array|\Dcat\Admin\Http\JsonResponse|mixed
     */
    public static function auditorAudit($auditor = [])
    {
        if (count($auditor) < 1) {
            return Admin::json()->error('节点类型不能为空！');
        }
        self::$AuditorArr = $auditor;
    }

    private static function isIgnoreAudit(): bool
    {
        return \Illuminate\Support\Facades\Request::has(Tree::SAVE_ORDER_NAME);
    }

    /**
     * @param string $permissions
     * @param string $name
     * @param string $url
     * @param string $canTrans
     * @param string $cannotTrans
     */
    public function parameterNotConfig(
        string $permissions,
        string $name,
        string $url,
        string $canTrans = 'common.config_empty',
        string $cannotTrans = 'common.config_empty_without_permission'
    )
    {
        $html = Admin::user()->can($permissions)
            ? trans($canTrans, [
                'name' => $name,
                'url' => $url
            ])
            : trans($cannotTrans, [
                'name' => $name
            ]);
        $this->html($html)->width(12);
        $this->disableFooter();
    }
    public function fileDetail(
        string            $field = 'files',
        string|array|null $path = null,
        string            $label = '',
        string            $name  = '',
    )
    {
        return $this->html(Show::make(null, null, function (Show $show)use($field,$path,$label,$name) {
            $show->disableHeader();
            $show->fileDetail($field,$path,$label,$name);
        })->render())->width(12);
    }

    /**
     * @param string $type
     * @param string|null $label
     * @param string $field
     * @param string $mime
     * @param int $maxSize
     * @param string|null $help
     */
    public function uploadMultipleFiles(
        string  $field,
        ?string $label = null,
        string  $mime = 'xlsx,xls,doc,docx,pdf,jpg,jpeg,png',
        int     $maxSize = 5120*40,
        int     $fileNumLimit = 100,
        ?string $help = null
    )
    {

        return $this->multipleFileView($field, $label)
            ->autoUpload()
            ->downloadable()
            ->autoSave(false)
            ->maxSize($maxSize)
            ->url(admin_url('file/uploads'))
            ->rules(['file', 'mimes:' . $mime])
            ->limit($fileNumLimit)
            ->help($help ?? trans('common.upload_file_help', [
                    'size' => round($maxSize / 1024, 2) . 'M'
                ])
            );
    }

    public function uploadMultipleFilesInvoce(
        string  $field,
        ?string $label = null,
        string  $mime = 'xlsx,xls,doc,docx,pdf,jpg,jpeg,png',
        int     $maxSize = 5120*40,
        int     $fileNumLimit = 100,
        ?string $help = null
    )
    {

        return $this->multipleFileView($field, $label)
            ->autoUpload()
            ->downloadable()
            ->autoSave(false)
            ->maxSize($maxSize)
            ->url(admin_url('file/uploads'))
            ->accept($mime)
            ->limit($fileNumLimit)
//            ->help($help ?? '请上传格式文件！', [
//                'size' => round($maxSize / 1024, 2) . 'M'
//            ]);
//            };\
            ->help($help ?? trans('common.upload_video_invoice', [
                'size' => round($maxSize / 1024, 2) . 'M',
                'accept' => $mime
                ])
            );
    }

    /**
     * @param string $type
     * @param string|null $label
     * @param string $field
     * @param string $mime
     * @param int $maxSize
     * @param string|null $help
     */
    public function uploadMultipleImages(
        string  $field,
        ?string $label = null,
        string  $mime = 'jpg,jpeg,png',
        int     $maxSize = 5120*5,
        int     $fileNumLimit = 100,
        ?string $help = null
    )
    {
        $this->multipleImage($field, $label)
            ->autoUpload()
            ->downloadable()
            ->autoSave(false)
            ->maxSize($maxSize)
            ->url(admin_url('file/uploads'))
            ->rules(['file', 'mimes:' . $mime])
            ->limit($fileNumLimit)
            ->help($help ?? trans('common.upload_img_help', [
                    'size' => round($maxSize / 1024, 2) . 'M'
                ])
            );
    }
    /**
     * @param string $type
     * @param string|null $label
     * @param string $field
     * @param string $mime
     * @param int $maxSize
     * @param string|null $help
     */
    public function uploadImage(
        string  $field,
        ?string $label = null,
        string  $mime = 'jpg,jpeg,png',
        int     $maxSize = 5120*5,
        int     $fileNumLimit = 100,
        ?string $help = null,
        bool|array    $compress = false
    )
    {
        $this->imageView($field, $label)->compress($compress)
            ->autoUpload()
            ->downloadable()
            ->autoSave(false)
            ->maxSize($maxSize)
            ->url(admin_url('file/uploads'))
            ->rules(['file', 'mimes:' . $mime])
            ->limit($fileNumLimit)
            ->help($help ?? trans('common.upload_img_help', [
                    'size' => round($maxSize / 1024, 2) . 'M'
                ])
            );
    }
}
